package com.chenfan.mcn.controller;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chenfan.common.annotations.DataPrivilege;
import com.chenfan.common.exception.BusinessException;
import com.chenfan.mcn.annotations.ActionTracing;
import com.chenfan.mcn.constant.NumberCst;
import com.chenfan.mcn.constant.SeparatorCst;
import com.chenfan.mcn.enums.IntentionOrderStatusEnum;
import com.chenfan.mcn.enums.OperateLockEnum;
import com.chenfan.mcn.exception.McnErrorCode;
import com.chenfan.mcn.extension.Res;
import com.chenfan.mcn.extension.excel.ExcelUtils;
import com.chenfan.common.extension.validation.groups.Add;
import com.chenfan.common.extension.lock.DistributedLockTemplate;
import com.chenfan.mcn.model.DocIntentionOrder;
import com.chenfan.mcn.model.DocIntentionOrderQuestion;
import com.chenfan.mcn.model.dto.*;
import com.chenfan.mcn.model.McnPage;
import com.chenfan.mcn.model.vo.*;
import com.chenfan.mcn.service.DocIntentionOrderService;
import com.chenfan.mcn.utils.CommonUtil;
import com.chenfan.common.extension.util.SmartCompletableFuture;
import com.chenfan.privilege.common.config.SearchAuthority;
import com.chenfan.privilege.common.config.UrlAuth;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 前置执行单
 * @author wulg
 * @date 2022/10/27
 */
@Slf4j
@RestController
@RequestMapping("/intentionOrder")
public class IntentionOrderController {

    @Autowired
    private DocIntentionOrderService intentionOrderService;

    /**
     * 新增
     * @param saveDTO
     * @return
     */
    @PostMapping("/add")
    @UrlAuth({"intentionOrderList_add","celebrityScheduling_add"})
    public Res<Long> add(@RequestBody IntentionOrderSaveDTO saveDTO) {
        Assert.notNull(saveDTO.getCustomerId(), "客户不能为空！");
        return Res.ok(intentionOrderService.add(saveDTO));
    }

    /**
     * 修改
     * @param saveDTO
     * @return
     */
    @PostMapping("/update")
    public Res<Long> update(@RequestBody IntentionOrderSaveDTO saveDTO) {
        Assert.notNull(saveDTO.getId(), "意向单ID不能为空！");
        return Res.ok(DistributedLockTemplate.execute(OperateLockEnum.INTENTION_ORDER.generateKey(saveDTO.getId()),
                () -> intentionOrderService.update(saveDTO)));
    }

    /**
     * 详情（适用于意向单列表/主播排期详情/移动端详情）
     * @param id 意向单ID
     * @param showTab 是否显示Tab 除了意向单管理详情是true，其它都是false
     * @return
     */
    @GetMapping("/detail")
    public Res<IntentionOrderDetailVO> detail(@RequestParam(name = "id") Long id, @RequestParam(name = "showTab", defaultValue = "true") Boolean showTab) {
        return Res.ok(intentionOrderService.detail(id, showTab));
    }

    /**
     * 批量详情
     * @param idList 意向单ID数组
     * @return
     */
    @PostMapping("/batchDetail")
    public Res<List<IntentionOrderDetailVO>> batchDetail(@RequestBody List<Long> idList) {
        Assert.notEmpty(idList, "请选择要查看的意向单");
        List<Long> newIdList = idList.stream().distinct().collect(Collectors.toList());
        return Res.ok(intentionOrderService.batchDetail(newIdList));
    }


    /**
     * 意向单列表(分页)
     * @param mcnPage
     * @param searchDTO
     * @return
     */
    @DataPrivilege(pageComponent = "intentionOrderList")
    @PostMapping("/page")
    public Res<Page<IntentionOrderListVO>> page(@ApiIgnore McnPage<IntentionOrderListVO> mcnPage, @SearchAuthority IntentionOrderSearchDTO searchDTO) {
        return Res.ok(intentionOrderService.page(mcnPage, searchDTO));
    }

    /**
     * 意向单列表(分页)
     * @param mcnPage
     * @param searchDTO
     * @return
     */
    @DataPrivilege(pageComponent = "intentionOrderList")
    @PostMapping("/intentionOrderPage")
    public Res<Page<IntentionOrderListVO>> intentionOrderPage(@ApiIgnore McnPage<IntentionOrderListVO> mcnPage, @SearchAuthority IntentionOrderSearchDTO searchDTO) {
        searchDTO.setCheck(true);
        return Res.ok(intentionOrderService.page(mcnPage, searchDTO));
    }

    /**
     * 弹框查询意向单
     * @param mcnPage
     * @param searchDTO
     * @return
     */
    @DataPrivilege(pageComponent = "intentionOrderList")
    @PostMapping("/queryBoxPage")
    public Res<Page<IntentionOrderListVO>> queryBoxPage(@ApiIgnore McnPage<IntentionOrderListVO> mcnPage, @SearchAuthority IntentionOrderSearchDTO searchDTO) {
        return Res.ok(intentionOrderService.page(mcnPage, searchDTO));
    }

    /**
     * 状态统计
     * @return
     */
    @DataPrivilege(pageComponent = "intentionOrderList")
    @GetMapping("/getCountByStatus")
    public Res<IntentionOrderStatusStatisVO> getCountByStatus(@SearchAuthority IntentionOrderSearchDTO searchDTO){
        return Res.ok(intentionOrderService.getCountByStatus(searchDTO));
    }

    /**
     * 模糊获取意向单code
     * @param size
     * @param likeCode
     * @return
     */
    @DataPrivilege(pageComponent = "intentionOrderList")
    @GetMapping("/getIntentionOrderCodes")
    public Res<List<String>> getIntentionOrderCodes(@Param("size")Integer size,@Param("likeCode")String likeCode, @SearchAuthority IntentionOrderSearchDTO searchDTO){
        return Res.ok(intentionOrderService.getIntentionOrderCodes(size,likeCode,searchDTO));
    }

    /**
     * 导出
     * @param searchDTO
     * @param response
     */
    @DataPrivilege(pageComponent = "intentionOrderList")
    @PostMapping("/export")
    @UrlAuth({"intentionOrderList_export"})
    public void export(@SearchAuthority IntentionOrderSearchDTO searchDTO, HttpServletResponse response) {
        List<IntentionOrderExportVO> exportList = CommonUtil.defaultList(intentionOrderService.export(searchDTO));
        ExcelUtils.exportExcel(exportList, "意向单", IntentionOrderExportVO.class, "意向单.xlsx", response);
    }

    /**
     * 保存并提交
     * @param saveDTO
     * @return
     */
    @PostMapping("/saveAndCommit")
    public Res<Long> saveAndCommit(@RequestBody @Validated({Add.class}) IntentionOrderSaveDTO saveDTO){
        Long id;
        if(saveDTO.getId() == null) {
            id = intentionOrderService.add(saveDTO);
            intentionOrderService.changeStatus(IntentionOrderStatusEnum.WAIT_ACCEPT, id, true);
        } else {
            id = saveDTO.getId();
            intentionOrderService.update(saveDTO);
        }
        return Res.ok(id);
    }

    /**
     * （批量）变更单据状态（适用于提交、接单、拒单、锁档、执行、作废操作）
     *
     * @param changeStatusDTO {@link IntentionOrderChangeStatusDTO}
     * @return {@link Res<Boolean>}
     */
    @PostMapping(value = "/changeStatus")
    @UrlAuth({"celebrityScheduling_invaild","celebrityScheduling_execution","intentionOrderList_goto","intentionOrderList_over",
            "intentionOrderList_execution","celebrityScheduling_submit","intentionOrderList_commit"})
    public Res<IntentionOrderChangeStatusResultVO> changeStatus(@RequestBody @Validated IntentionOrderChangeStatusDTO changeStatusDTO) {
        IntentionOrderStatusEnum statusEnum = IntentionOrderStatusEnum.toEnum(changeStatusDTO.getStatus());
        Assert.notNull(statusEnum, "非法单据状态");
        IntentionOrderChangeStatusResultVO resultVO = new IntentionOrderChangeStatusResultVO();
        resultVO.setTotalNum(changeStatusDTO.getIdList().size());
        List<String> failReasonList = new CopyOnWriteArrayList<>();
        List<Long> changeIdList = new CopyOnWriteArrayList<>();
        AtomicInteger successAtomicInteger = new AtomicInteger(NumberCst.ZERO);
        AtomicInteger failAtomicInteger = new AtomicInteger(NumberCst.ZERO);
        List<CompletableFuture> completableFutureList = new ArrayList<>(changeStatusDTO.getIdList().size());
        changeStatusDTO.getIdList().forEach(id -> {
            completableFutureList.add(SmartCompletableFuture.runAsync(() -> {
                try {
                    Long changeId = DistributedLockTemplate.execute(OperateLockEnum.INTENTION_ORDER.generateKey(id),
                            () -> intentionOrderService.changeStatus(statusEnum, id,
                                    ObjectUtils.defaultIfNull(changeStatusDTO.getConfirmDiff(), Boolean.FALSE).booleanValue()));
                    if(Objects.nonNull(changeId)) {
                        changeIdList.add(changeId);
                    } else {
                        successAtomicInteger.incrementAndGet();
                    }
                } catch (Exception e) {
                    failAtomicInteger.incrementAndGet();
                    if(e instanceof BusinessException) {
                        failReasonList.add(e.getMessage());
                    } else {
                        failReasonList.add("操作失败");
                    }
                }
           }));
        });
        SmartCompletableFuture.allOf(completableFutureList.toArray(new CompletableFuture[]{})).join();
        resultVO.setSuccessNum(successAtomicInteger.get());
        resultVO.setFailNum(failAtomicInteger.get());
        resultVO.setChangeNum(changeIdList.size());
        resultVO.setChangeIdList(changeIdList);
        resultVO.setFailReasonList(failReasonList);
        return Res.ok(resultVO);
    }

    /**
     * 撤回
     * @param id
     * @return
     */
    @ActionTracing("撤回")
    @GetMapping(value = "/withdrawal")
    @UrlAuth({"intentionOrderList_withdrawal"})
    public Res<Long> withdrawal(@RequestParam(name = "id") Long id) {
        return Res.ok(DistributedLockTemplate.execute(OperateLockEnum.INTENTION_ORDER.generateKey(id),
                () -> intentionOrderService.changeStatus(IntentionOrderStatusEnum.DRAFT, id, false)));
    }

    /**
     * 创建合同(前端不调用了)
     *
     * @param addContractDTO {@link IntentionOrderChangeStatusDTO}
     * @return {@link Res<Boolean>}
     */
    @PostMapping(value = "/addContract")
    @UrlAuth({"intentionOrderList_addContract"})
    public Res<Long> addContract(@RequestBody @Validated IntentionOrderAddContractDTO addContractDTO) {
        return Res.ok(DistributedLockTemplate.execute(OperateLockEnum.INTENTION_ORDER.generateKey("addContract"),
                () -> intentionOrderService.addContract(addContractDTO)));
    }

    /**
     * 博主排期
     * @param searchDTO
     * @return
     */
    @DataPrivilege(pageComponent = "intentionOrderList")
    @PostMapping("/anchorScheduling")
    @UrlAuth({"celebrityScheduling_search"})
    public Res<AnchorSchedulingCalendarVO> anchorScheduling(@SearchAuthority AnchorSchedulingSearchDTO searchDTO) {
        Assert.notNull(searchDTO.getMonth(), "请选择月份");
        return Res.ok(intentionOrderService.anchorScheduling(searchDTO));
    }

    /**
     * 意向询单(分页)
     * @param searchDTO
     * @return
     */
    @DataPrivilege(pageComponent = "intentionOrderList")
    @PostMapping("/inquiryPage")
    @UrlAuth({"celebrityScheduling_search"})
    public Res<Page<AnchorSchedulingCalendarVO.IntentionOrderData>> inquiryPage(@SearchAuthority AnchorSchedulingSearchDTO searchDTO) {
        return Res.ok(intentionOrderService.inquiryPage(searchDTO));
    }

    /**
     * 意向单作废时二次确认
     * @param idList 意向单ID 数组
     * @return
     */
    @PostMapping("/invalidConfirm")
    public Res<String> invalidConfirm(@RequestBody List<Long> idList) {
        Assert.notEmpty(idList, "请选择要做作废的意向单");
        List<DocIntentionOrder> dataList = intentionOrderService.selectList(Wrappers.lambdaQuery(DocIntentionOrder.class)
                .in(DocIntentionOrder::getId, idList).isNotNull(DocIntentionOrder::getIncomeContractId)
                .eq(DocIntentionOrder::getIsDelete, NumberCst.ZERO));
        if(CollectionUtils.isNotEmpty(dataList)) {
            String errorMsg = dataList.stream().map(DocIntentionOrder::getIntentionCode).collect(Collectors.joining(SeparatorCst.OBLIQUE_LINE));
            return Res.failed(McnErrorCode.INTENTION_ORDER_EXIST_INCOME_CONTRACT, "意向单【"+errorMsg+"】已关联收入合同，请问是否继续！");
        }
        return Res.ok();
    }

    /**
     * （前置接口）是否允许批量接单
     * @param idList 意向单ID 数组
     * @return
     */
    @PostMapping("/allowBatchAccept")
    public Res<Object> allowBatchAccept(@RequestBody List<Long> idList) {
        Assert.notEmpty(idList, "请选择要接单的意向单");
        String splicedCode = intentionOrderService.getSplicedCodeFromQuestion(idList);
        if(StringUtils.isNotBlank(splicedCode)) {
            return Res.failed(McnErrorCode.NOT_ALLOWED_BATCH_ACCEPT_ORDER, "【"+splicedCode+"】存在询单问题不允许批量接单！");
        }
        return Res.ok();
    }

    /**
     * 根据意向单ID获取意向单问题
     * @param id 意向单ID
     * @return
     */
    @GetMapping("/getQuestionsById")
    public Res<List<DocIntentionOrderQuestion>> getQuestionsById(@RequestParam(name = "id") Long id) {
        Assert.notNull(id, "请选择意向单");
        return Res.ok(intentionOrderService.getQuestionsById(id));
    }

    /**
     * 单个接单(功能废弃了)
     * @param dto
     * @return
     */
    @Deprecated
    @PostMapping("/acceptOrder")
    @UrlAuth({"celebrityScheduling_order"})
    public Res<Boolean> acceptOrder(@Validated @RequestBody IntentionOrderAcceptDTO dto) {
        return Res.ok(intentionOrderService.acceptOrder(dto));
    }

    /**
     * 获取意向单列表查询金额汇总
     * @param searchDTO
     * @return
     */
    @DataPrivilege(pageComponent = "intentionOrderList")
    @PostMapping("/getAmountSummary")
    public Res<IntentionOrderAmountSummaryVO> getAmountSummary(@SearchAuthority IntentionOrderSearchDTO searchDTO) {
        return Res.ok(intentionOrderService.getAmountSummary(searchDTO));
    }
}
